/**
 * 源文件实体对象
 * @public 
 * @constructor
 * @param {String} source 源文件内容
 */
function SourceEntry(source)
{
	//var timestamp = new Date().getTime();
	this.init(source);
	this.parse();
	this.docEntries = [];
	this.docIds = [];
	this.docMap = {};
	this.filedocs = [];
	
	var partitions = this.partitions
		, docEntries = this.docEntries
		, i = 0
		, len = partitions.length
		, tmpObj;

	for (; i < len; i++)
	{
		tmpObj = partitions[i];

		if (tmpObj.type == "document")
		{
			docEntries.push(new DocEntry(this, tmpObj.begin, tmpObj.end));
		}
	}

	for (i = 0, len = docEntries.length; i < len; i++)
	{
		tmpObj = docEntries[i];

		if (tmpObj.isFiledoc())
		{
			this.filedocs.push(tmpObj);
		}
		else
		{
			var id = tmpObj.getId();
			this.docMap[id] = tmpObj;
			this.docIds.push(id);
		}
	}
	//this.timeSpent = new Date().getTime() - timestamp;
	//$log.info(this.timeSpent);
}

SourceEntry.sourceEntryMap = {};

SourceEntry.require = function(file, pkg)
{
	pkg = pkg ? $import(pkg + ":") : {scriptBase : "", name : ":"}

	var path = pkg.scriptBase + file
		, source = this.sourceEntryMap[path];

	if (!source)
	{
		var text = $JSI.getCacheScript(pkg.name, file);

		if (typeof text != "string")//maybe it is an function or object
		{
			//$log.debug("加载源码" + pkg.name + "\t" + file, "文件路径" + path);
			text = $JSI.loadURL(path);
		}
		source = this.sourceEntryMap[path] = new SourceEntry(text);
	}
	return source;
}

var $p = (Type.extend(SourceEntry, ECMAParser)).prototype;

$p.getDescription = function(id)
{
	if (!("_description" in this))
	{
		var infos = []
			, i = 0
			, docLen = this.filedocs.length;

		for (; i < docLen; i++)
		{
			infos.push(this.filedocs[i].description || "");
		}
		this._description = infos.join("\r\n");

		if (infos.length == 0 && this.partitions.length)
		{
			var t = this.partitions[0], type = t.type;

			if (type == "comment")
			{
				this._description = t.value.replace(/^\s*\/\//, "");
			}
			else if (type == "muti-comment")
			{
				this._description = t.value.replace(/(?:^\s*\/\*)|(?:\*\/\s*$)/g, "").replace(/^\s*\*\s?/gm, "");
			}
		}
	}
	return this._description;
}

$p.getDocEntry = function(id)
{
	return this.docMap[id];
}

$p.getTopDocEntries = function(id)
{
	var rtv = []
		, docEntries = this.docEntries
		, docEntry
		, i = docEntries.length - 1;

	for (; i >= 0; i--)
	{
		if ((docEntry = docEntries[i]).isTop())
		{
			rtv.push(docEntry);
		}
	}
	return rtv;
}

function DocEntry(sourceEntry, start, end)
{
	this.sourceEntry = sourceEntry;
	this.source = sourceEntry.source;
	this.start = start;
	this.end = end;
	this.tagAttributes =
	{
		"constructor": null
	};

	this.sourceAttributes =
	{
		"constructor": null
	};

	this.parseSource();
	this.parseDoc();
}

$p = DocEntry.prototype;

$p.getStaticMemberMap = function()
{
	if (!this.staticMemberMap)
	{
		var sourceEntry = this.sourceEntry
			, ids = sourceEntry.docIds
			, id
			, i = ids.length - 1
			, map = this.staticMemberMap = {}
			, staticRe = new RegExp("^" + this.getId().replace(/([\$\.])/g, "\\$1") + "\\.([^\\.]+)$");

		for (; i >= 0; i--)
		{
			id = ids[i];
			if (staticRe.test(id))
			{

				map[id.replace(staticRe, "$1")] = sourceEntry.getDocEntry(id);
			}
		}
	}
	return map;
}
$p.getInstanceMemberMap = function()
{
	if (! this.instanceMemberMap)
	{
		var ids = this.sourceEntry.docIds
			, instanceMemberMap = this.instanceMemberMap = {}
			, propRe = new RegExp("^" + this.getId().replace(/([\$\.])/g, "\\$1") + "\\.prototype\\.([^\\.]+)$")

		for (var i = ids.length - 1; i >= 0; i--)
		{
			if (propRe.test(ids[i]))
			{
				instanceMemberMap[ids[i].replace(propRe, "$1")] = this.sourceEntry.getDocEntry(ids[i]);
			}
		}
	}
	return instanceMemberMap;
}

$p.toString = function()
{
	var buf = ["sourceId=" + this.sourceAttributes.name + "\ndescription=" + this.description + "\n"];

	for (var key in this.tagAttributes)
	{
		buf.push("@" + key + ":" + this.tagAttributes[key] + "\n");
	}
	buf.push("\n\n\n");
	return buf.join("");
}

$p.parseDoc = function()
{
	this.doc = this.source.substring(this.start, this.end);
	var pp = /^\s*\*\s*(?:@([\w\d\-_]+))?\s*((?:(?:{@)|[^@])*)$/gm
		, m;

	while (m = pp.exec(this.doc))
	{
		var part = m[0]
			, tag = m[1]
			, content = m[2];

		if (content)
		{
			content = content.replace(/(?:\s*\*\/\s*$)|(?:^\s*\*\s?)/gm, "");
		}

		if (tag)
		{
			switch (tag)
			{
				case "public":
				case "private":
				case "protected":
				case "friend":
				case "intenal":
					this.processTag("access", tag);
					break;

				default:
					this.processTag(tag, content);
			}
		}
		else
		{
			this.description = content;
		}
	}
};
$p.tagAlias = function()
{
	var list = [accessTag, flagTag, valueTag, valuesTag]
		,rtv =
		{
			"constructor": null
		}
		, i = list.length - 1
		, name;

	for (; i >= 0; i--)
	{
		for (name in list[i])
		{
			var info = list[i][name];

			if (info.alias)
			{
				for (var j = info.alias.length - 1; j >= 0; j--)
				{
					rtv[info.alias[j]] = name;
				}
			}
		}
	}
	return rtv;
}();

$p.processTag = function(tag, value)
{
	if (this.tagAlias[tag])
	{
		tag = this.tagAlias[tag];
	}
	var values = this.tagAttributes[tag];

	if (values == null)
	{
		values = this.tagAttributes[tag] = [];
	}
	values.push(value);
}

var FUN_1 = /^\s*function\s+[\w\$]+\s*\([\w\$,\s]*\)/
	, FUN_2 = /^\s*function\s*\([\w\$,\s]*\)/
	, OBJ_1 = /^\s*var\s+[\w\$]+\s*=/
	, OBJ_2 = /^\s*[\w\$\.]+\s*=/
	, OBJ_3 = /^\s*[\w\$]+\s*:/;
$p.parseSource = function()
{
	var source = this.source.substr(this.end)
		, name
		, type
		, instance
		, pos;

	if (FUN_1.test(source))
	{
		pos = source.indexOf("function");
		name = source.substring(pos + "function".length, source.indexOf("(")).replace(/\s*/g, "");
		type = "function";
	}
	else
	{
		if (OBJ_1.test(source))
		{ //var xxx
			pos = source.indexOf("=");
			name = source.substring(source.indexOf("var") + 3, pos).replace(/\s*/g, "");
		}
		else if (OBJ_2.test(source))
		{
			pos = source.indexOf("=");
			name = source.substring(0, pos).replace(/\s*/g, "");
		}
		else if (OBJ_3.test(source))
		{
			pos = source.indexOf(":");
			name = source.substring(0, pos).replace(/\s*/g, "");
		}
		source = source.substr(pos + 1);

		if (FUN_2.test(source))
		{
			type = "function";
		}
		else if (/^\s*(\[|\{|(new\s+))/.test(source))
		{
			type = "object";
		}
	}

	if (name && name.length != (name = name.replace(/^this\./, "")).length)
	{
		this.sourceAttributes.instanceAttribute = true;
	}
	this.sourceAttributes.name = name;
	this.sourceAttributes["typeof"] = type;
}

$p.getAttribute = function(key)
{
	var values = this.tagAttributes[key];

	return values ? values[values.length - 1] : null;
}

$p.getAttributeType = function()
{
	return this.getAttribute("attributeType");
}

$p.getExtend = function()
{
	var e = this.getAttribute("extend");

	if (e)
	{
		return e.replace(/^\s*([\w\.]+)[\s\S]*$/, "$1");
	}
}

$p.isConstructor = function()
{
	return this.tagAttributes["constructor"] != null;
}

$p.getInstanceof = function()
{
	return this.getAttribute("instanceof");
}

$p.getTypeof = function()
{
	return this.getAttribute("typeof");
}

$p.getAccess = function()
{
	return this.getAttribute("access");
}

$p.getOwner = function()
{
	var o = this.getAttribute("owner");
	return o ? o.replace(/([\w\d\.\$\_]*)[\s\S]*$/, "$1") : null;
}

$p.getParams = function()
{
	return this.tagAttributes["param"];
}

$p.getDescription = function()
{
	return this.description;
}

$p.getArguments = function()
{
	return this.getAttribute("arguments");
}

$p.getReturn = function()
{
	return this.getAttribute("return");
}

$p.getReturnType = function()
{
	return this.getAttribute("returnType");
}

$p.getStatic = function()
{
	return this.tagAttributes["static"] && true;
}

$p.getName = function()
{
	var name = this.getAttribute("name");

	return name ? name : this.sourceAttributes["name"];
};

$p.isFileoverview = function()
{
	return this.getAttribute("fileoverview") && true;
}

$p.getId = function()
{
	if (this.id)
	{
		return this.id;
	}

	if (this.tagAttributes.id)
	{
		return this.id = this.tagAttributes.id[0].replace(/\s+/g, "");
	}
	var name = this.getName();

	if (!name)
	{
		return null;
	}
	var owner = this.getOwner();

	if (this.sourceAttributes.instanceAtribute)
	{
	//TODO...
	/*
	  if(!owner){
		if(this.owner == undefined){
		  var des = this.sourceEntry.docEntries;
		  for(var i = des.length-1;i>=0;i--){
			var doc = des[i];
			if(doc == this){
			  for(i--;i>=0;i--){
				var doc = des[i];
				if(doc.isConstructor()){
				  owner = this.owner = doc.getId();
				}else{
				  owner = doc.getOwner();
				  if(owner){
					this.owner = owner;
				  }else{
					doc.getId();
					owner = this.owner = doc.owner;
				  }
				}
			  }
			}
		  }
		}
	  }
	  */
	}

	if (owner)
	{
		if (! this.sourceAttributes.instanceAtribute && this.getStatic())
		{
			this.id = owner + "." + name;
		}
		else
		{
			this.id = owner + ".prototype." + name;
		}
		return this.id;
	}
	return this.id = name;
}

$p.isTop = function()
{
	if (this.isConstructor())
	{
		return true;
	}
	else
	{
		var id = this.getId();
		return this.getOwner() == null && id != null && id.indexOf(".") < 0;
	}
}

$p.isFiledoc = function()
{
	return this.isFileoverview() || this.getId() == null;
}

$p = null;